package org.ftc.web;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.docx4j.XmlUtils;
import org.docx4j.jaxb.Context;
import org.docx4j.model.fields.merge.DataFieldName;
import org.docx4j.model.fields.merge.MailMerger;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.Part;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.Br;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.docx4j.wml.STBrType;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.Tc;
import org.docx4j.wml.Text;
import org.docx4j.wml.Tr;
import org.ftc.domain.CodeType;
import org.ftc.domain.CodeUnit;
import org.ftc.domain.Committee;
import org.ftc.domain.Motion;
import org.ftc.domain.Session;
import org.ftc.domain.Users;
import org.ftc.form.MotionForm;
import org.ftc.service.CodeTypeService;
import org.ftc.service.CodeUnitService;
import org.ftc.service.CommitteeService;
import org.ftc.service.FtcLogService;
import org.ftc.service.FtcLogService.Log;
import org.ftc.service.MotionService;
import org.ftc.service.SessionService;
import org.ftc.service.UsersService;
import org.ftc.util.Global;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class ReportController {
	@Autowired
	UsersService usersService;
	@Autowired
	SessionService sessionService;
	@Autowired
	MotionService motionService;
	@Autowired
	CodeTypeService codeTypeService;
	@Autowired
	CodeUnitService codeUnitService;
	@Autowired
	CommitteeService committeeService;
	@Autowired
	FtcLogService logService;

	public enum DocCode { // 公報格式
		/**
		 * "項一", "%s、%s:%s"
		 */
		ITEM1("項一", "%s、%s：%s"), //
		/**
		 * "案", "%s:"
		 */
		CASE("案", "%s："), //
		/**
		 * "項二", "%s、%s"
		 */
		ITEM2("項二", "%s、%s"), //
		/**
		 * "決一", "%s:%s"
		 */
		DECIDE1("決一", "%s：%s"), //
		/**
		 * "決二", "%s"
		 */
		DECIDE2("決二", "%s"), //
		/**
		 * "決三", "%s"
		 */
		DECIDE3("決三", "%s"), //
		/**
		 * "附一", "附帶決議：%s"
		 */
		SUB1("附一", "附帶決議：%s"), //
		/**
		 * "附二", "%s"
		 */
		SUB2("附二", "%s"),
		/**
		 * "附三", "%s"
		 */
		SUB3("附三", "%s"),
		/**
		 * 玖、主席裁示：無。
		 */
		TAIL1("玖、主席裁示：無。", ""),
		/**
		 * 拾、散會
		 */
		TAIL2("拾、散會", ""),
		/**
		 * 玖、主席裁示：無。
		 */
		TAIL3("拾、主席裁示：無。", ""),
		/**
		 * 拾、散會
		 */
		TAIL4("拾壹、散會", ""),
		/**
		 * 拾、案由
		 */
		CAUSE("案由", "案由：%s");

		final String code;

		final String format;

		DocCode(String code, String format) {
			this.code = code;
			this.format = format;
		}

	}

	static Map<String, List<Motion>> mainMap = new LinkedHashMap<>();

	static Map<String, List<Motion>> getNewMainMap() {
		mainMap = new LinkedHashMap<>();
		mainMap.put("0", new ArrayList<Motion>());
		mainMap.put("3", new ArrayList<Motion>());
		mainMap.put("9", new ArrayList<Motion>());
		return mainMap;
	}

	static String[] NUM = { "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", //
			"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", //
			"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", //
			"1", "2", "3", "4", "5", "6", "7", "8", "9", "0" };
	static String[] CHINESE_NUM = { "三十", "三十一", "三十二", "三十三", "三十四", "三十五", "三十六", "三十七", "三十八", "三十九", //
			"二十", "二十一", "二十二", "二十三", "二十四", "二十五", "二十六", "二十七", "二十八", "二十九", //
			"十", "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", //
			"一", "二", "三", "四", "五", "六", "七", "八", "九", "０" };

	@RequestMapping(value = "/report/{sessionNo}.htm")
	public String index(@PathVariable String sessionNo, ModelMap model) {
		model.addAttribute("sessionNo", sessionNo);//
		return "report/list";
	}

	@RequestMapping(value = "/report/motionList.htm", method = RequestMethod.POST)
	public void motionList(MotionForm form, ModelMap model, HttpServletRequest request, HttpServletResponse response) {
		logService.before();
		List<Motion> datas = motionService.findAll(form);
		XSSFWorkbook workbook = new XSSFWorkbook();
		XSSFSheet sheet = workbook.createSheet("sheet1");
		int rownum = 0, cellnum1 = 0;
		Row row1 = sheet.createRow(rownum++);
		Cell cell1 = row1.createCell(cellnum1++);
		cell1.setCellValue("期別");
		cell1 = row1.createCell(cellnum1++);
		cell1.setCellValue("案別");
		cell1 = row1.createCell(cellnum1++);
		cell1.setCellValue("案號");
		cell1 = row1.createCell(cellnum1++);
		cell1.setCellValue("處室");
		cell1 = row1.createCell(cellnum1++);
		cell1.setCellValue("分機");
		cell1 = row1.createCell(cellnum1++);
		cell1.setCellValue("承辦人");
		cell1 = row1.createCell(cellnum1++);
		cell1.setCellValue("案由");
		cell1 = row1.createCell(cellnum1++);
		cell1.setCellValue("決議");
		for (Motion motion : datas) {
			Row row = sheet.createRow(rownum++);
			int cellnum = 0;
			Cell cell = row.createCell(cellnum++);
			cell.setCellValue(motion.getSession().getSessionNo());
			cell = row.createCell(cellnum++);
			CodeType type = codeTypeService.get(motion.getTypeNo());
			cell.setCellValue(type == null ? "" : (StringUtils.isBlank(type.getName()) ? "" : type.getName()));
			cell = row.createCell(cellnum++);
			cell.setCellValue(motion.getMotionNo());
			cell = row.createCell(cellnum++);
			CodeUnit unit = codeUnitService.get(motion.getUnitCode());
			cell.setCellValue(unit == null ? "" : (StringUtils.isBlank(unit.getName()) ? "" : unit.getName()));
			cell = row.createCell(cellnum++);
			cell.setCellValue(motion.getTelNo());
			cell = row.createCell(cellnum++);
			cell.setCellValue(motion.getInCharge());
			cell = row.createCell(cellnum++);
			cell.setCellValue(motion.getCause());
			cell = row.createCell(cellnum++);
			cell.setCellValue(motion.getResolution());
		}
		try {
			String name = URLEncoder.encode("清單", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".xlsx");
			workbook.write(response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	// 目次表一
	@RequestMapping(value = "/report/{sessionNo}/report1.htm", method = RequestMethod.GET)
	public void report1(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		Session session = sessionService.get(sessionNo);
		try {
			File tmp = new File(Global.APP.REPORT_PATH, "目次表1.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);

			// 變數
			Map<DataFieldName, String> map = new HashMap<DataFieldName, String>();
			map.put(new DataFieldName("sessionNo"), sessionNo);
			map.put(new DataFieldName("date"), genDateFormat1(session.getMeetingDate()));

			Part part = template.getMainDocumentPart();
			List<Object> ps = getAllElementFromObject(part, P.class);

			Map<String, String> map2;
			String typeNo = "@";
			try {
				for (Motion motion : motions) {
					map2 = new HashMap<>();
					if (!StringUtils.equals(typeNo, motion.getTypeNo())) {
						if (StringUtils.equals(motion.getTypeNo(), "0")) {
							map2.put("事項", "報告事項：");
							cloneP(template, ps, "事項", map2);
							map2.put("案", "報告案：");
							cloneP(template, ps, "案", map2);
						} else if (StringUtils.equals(motion.getTypeNo(), "3")) {
							map2.put("事項", "討論事項：");
							cloneP(template, ps, "事項", map2);
							map2.put("案", "審議案：");
							cloneP(template, ps, "案", map2);
						} else {
							map2.put("事項", "討論事項：");
							cloneP(template, ps, "事項", map2);
							map2.put("案", "臨時動議：");
							cloneP(template, ps, "案", map2);
						}

					}
					map2 = new HashMap<>();
					map2.put("壹", genChineseNum(motion.getMotionOrder()));
					map2.put("案由", motion.getCause());
					map2.put("date1", genDateFormat3(motion.getDate1(), "."));
					map2.put("處罰", StringUtils.isBlank(motion.getIsLegal()) ? "" : motion.getIsLegal());
					map2.put("單位", codeUnitService.get(motion.getUnitCode()).getName());
					map2.put("date2", genDateFormat3(motion.getDate2(), "."));
					map2.put("委員", committeeService.committeesName(motion.getExaminant()));
					cloneP(template, ps, "壹", map2);
					typeNo = motion.getTypeNo();
				}
				String[] strs = { "事項", "案", "壹" };
				removeP(template, ps, strs);
			} catch (JAXBException e) {
				e.printStackTrace();
			}
			File result = File.createTempFile("report1", UUID.randomUUID() + ".docx");
			MailMerger.performMerge(template, map, true);
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_目次表1", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 目次表二
	@RequestMapping(value = "/report/{sessionNo}/report2.htm", method = RequestMethod.GET)
	public void report2(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		Session session = sessionService.get(sessionNo);
		try {
			File tmp = new File(Global.APP.REPORT_PATH, "目次表2.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);
			// 變數
			Map<DataFieldName, String> map = new HashMap<DataFieldName, String>();
			map.put(new DataFieldName("sessionNo"), sessionNo);
			map.put(new DataFieldName("date"), genDateFormat1(session.getMeetingDate()));

			Part part = template.getMainDocumentPart();
			List<Object> ps = getAllElementFromObject(part, P.class);
			String typeNo = "@";
			Map<String, String> map2;
			try {
				for (Motion motion : motions) {
					map2 = new HashMap<>();
					if (!StringUtils.equals(typeNo, motion.getTypeNo())) {
						if (StringUtils.equals(motion.getTypeNo(), "0")) {
							map2.put("事項", "報告事項：");
							cloneP(template, ps, "事項", map2);
							map2.put("案", "報告案：");
							cloneP(template, ps, "案", map2);
						} else if (StringUtils.equals(motion.getTypeNo(), "3")) {
							map2.put("事項", "討論事項：");
							cloneP(template, ps, "事項", map2);
							map2.put("案", "審議案：");
							cloneP(template, ps, "案", map2);
						} else {
							map2.put("事項", "討論事項：");
							cloneP(template, ps, "事項", map2);
							map2.put("案", "臨時動議：");
							cloneP(template, ps, "案", map2);
						}

					}
					map2 = new HashMap<>();
					map2.put("壹", genChineseNum(motion.getMotionOrder()));
					map2.put("案由", motion.getCause());
					// 法律事務處216陳世芳／侯文賢 | 服務業競爭處314沈立委／陳俊廷代
					String str = "".concat(codeUnitService.get(motion.getUnitCode()).getName())
							.concat(motion.getTelNo()).concat(motion.getInCharge()).concat("／")
							.concat(motion.getSectionChief());
					if (StringUtils.isNoneBlank(motion.getUnitCode1())) {
						str = str.concat(" | ").concat(codeUnitService.get(motion.getUnitCode1()).getName())
								.concat(motion.getTelNo1()).concat(motion.getInCharge1()).concat("／")
								.concat(motion.getSectionChief1());
					}
					if (StringUtils.isNoneBlank(motion.getUnitCode2())) {
						str = str.concat(" | ").concat(codeUnitService.get(motion.getUnitCode2()).getName())
								.concat(motion.getTelNo2()).concat(motion.getInCharge2()).concat("／")
								.concat(motion.getSectionChief2());
					}
					map2.put("承辦", str);
					cloneP(template, ps, "壹", map2);
					typeNo = motion.getTypeNo();
				}
				String[] strs = { "事項", "案", "壹" };
				removeP(template, ps, strs);
			} catch (JAXBException e) {
				e.printStackTrace();
			}
			File result = File.createTempFile("report2", UUID.randomUUID() + ".docx");
			MailMerger.performMerge(template, map, true);
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_目次表2", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 目次表四
	@RequestMapping(value = "/report/{sessionNo}/report3.htm", method = RequestMethod.GET)
	public void report3(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		Session session = sessionService.get(sessionNo);
		List<Committee> committees = committeeService.findByCode(session.getCommitteeTeam());

		try {
			File tmp = new File(Global.APP.REPORT_PATH, "目次表4.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);

			// 變數
			Map<DataFieldName, String> map = new HashMap<DataFieldName, String>();
			map.put(new DataFieldName("sessionNo"), sessionNo);
			map.put(new DataFieldName("date"), genDateFormat4(session.getMeetingDate()));
			map.put(new DataFieldName("committee"), CollectionUtils.isNotEmpty(committees) ? committees.get(0)
					.getName() : "");

			List<Map<String, String>> list2 = new ArrayList<>();
			List<Map<String, String>> list3 = new ArrayList<>();
			Map<String, String> map2;
			for (Motion motion : motions) {
				map2 = new HashMap<>();
				if (StringUtils.equals(motion.getTypeNo(), "0")) {
					map2.put("n1", genChineseNum(motion.getMotionOrder()));
					map2.put("cause", motion.getCause());
					map2.put("committees", committeeService.committeesName(motion.getExaminant()));
					map2.put("unit1", codeUnitService.get(motion.getUnitCode()).getName());
					map2.put("charge1", motion.getInCharge());
					map2.put("chief1", motion.getSectionChief());
					if (StringUtils.isNotBlank(motion.getTelNo1())) {
						map2.put("sep1", "-----");
						CodeUnit codeUnit = codeUnitService.get(motion.getUnitCode1());
						map2.put("unit2", codeUnit == null ? "" : codeUnit.getName());
						map2.put("charge2", motion.getInCharge1());
						map2.put("chief2", motion.getSectionChief1());
					}
					if (StringUtils.isNotBlank(motion.getTelNo2())) {
						map2.put("sep2", "-----");
						CodeUnit codeUnit = codeUnitService.get(motion.getUnitCode2());
						map2.put("unit3", codeUnit == null ? "" : codeUnit.getName());
						map2.put("charge3", motion.getInCharge2());
						map2.put("chief3", motion.getSectionChief2());
					}
					list2.add(map2);
				} else {
					map2.put("n2", genChineseNum(motion.getMotionOrder()));
					map2.put("cause", motion.getCause());
					map2.put("committees", committeeService.committeesName(motion.getExaminant()));
					map2.put("unit1", codeUnitService.get(motion.getUnitCode()).getName());
					map2.put("charge1", motion.getInCharge());
					map2.put("chief1", motion.getSectionChief());
					if (StringUtils.isNotBlank(motion.getTelNo1())) {
						map2.put("sep1", "-----");
						CodeUnit codeUnit = codeUnitService.get(motion.getUnitCode1());
						map2.put("unit2", codeUnit == null ? "" : codeUnit.getName());
						map2.put("charge2", motion.getInCharge1());
						map2.put("chief2", motion.getSectionChief1());
					}
					if (StringUtils.isNotBlank(motion.getTelNo2())) {
						map2.put("sep2", "-----");
						CodeUnit codeUnit = codeUnitService.get(motion.getUnitCode2());
						map2.put("unit3", codeUnit == null ? "" : codeUnit.getName());
						map2.put("charge3", motion.getInCharge2());
						map2.put("chief3", motion.getSectionChief2());
					}
					list3.add(map2);
				}
			}
			Part p = template.getMainDocumentPart();
			List<Object> tables = getAllElementFromObject(p, Tbl.class);
			Tbl tempTable;
			try {
				tempTable = getTemplateTable(tables, "n1");
				replaceAndCloneTr(tempTable, list2, template);
				tempTable = getTemplateTable(tables, "n2");
				replaceAndCloneTr(tempTable, list3, template);
			} catch (JAXBException e) {
				e.printStackTrace();
			}

			MailMerger.performMerge(template, map, true);
			File result = File.createTempFile("report3", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_目次表4", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 封面
	@RequestMapping(value = "/report/{sessionNo}/report4.htm", method = RequestMethod.GET)
	public void report4(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		Session session = sessionService.get(sessionNo);
		try {
			File tmp = new File(Global.APP.REPORT_PATH, "封面.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);
			// 變數
			List<Map<DataFieldName, String>> list = new ArrayList<Map<DataFieldName, String>>();
			Map<DataFieldName, String> map;
			for (Motion motion : motions) {
				map = new HashMap<DataFieldName, String>();
				map.put(new DataFieldName("sessionNo"), sessionNo);
				map.put(new DataFieldName("date"), genDateFormat3(session.getMeetingDate(), "."));
				map.put(new DataFieldName("type"), motion.getType() + genChineseNum(motion.getMotionOrder()));
				map.put(new DataFieldName("cause"), motion.getCause());
				list.add(map);
			}
			WordprocessingMLPackage output = MailMerger.getConsolidatedResultCrude(template, list, true);
			File result = File.createTempFile("report4", UUID.randomUUID() + ".docx");
			output.save(result);
			String name = URLEncoder.encode(sessionNo + "_封面", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 議程封面
	@RequestMapping(value = "/report/{sessionNo}/report5.htm", method = RequestMethod.GET)
	public void report5(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		Session nowSession = sessionService.get(sessionNo);
		try {
			File tmp = new File(Global.APP.REPORT_PATH, "議程封面.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);
			// 變數
			Map<DataFieldName, String> map = new HashMap<DataFieldName, String>();
			map.put(new DataFieldName("sessionNo"), nowSession.getSessionNo());
			map.put(new DataFieldName("year"), nowSession.getYear());
			map.put(new DataFieldName("month"), nowSession.getMonth());
			map.put(new DataFieldName("day"), nowSession.getDay());

			MailMerger.performMerge(template, map, true);
			File result = File.createTempFile("report5", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_光碟目次", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 光碟封面
	@RequestMapping(value = "/report/{sessionNo}/report6.htm", method = RequestMethod.GET)
	public void report6(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		Session nowSession = sessionService.get(sessionNo);
		Session newSession = sessionService.findNext(nowSession);
		try {
			File tmp = new File(Global.APP.REPORT_PATH, "光碟封面.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);
			// 變數
			Map<DataFieldName, String> map = new HashMap<DataFieldName, String>();
			map.put(new DataFieldName("sessionNo"), ReportController.genChineseNum(nowSession.getSessionNo()));
			map.put(new DataFieldName("date1"), genDateFormat1(nowSession.getMeetingDate()));
			map.put(new DataFieldName("date1_2"), genDateFormat2(nowSession.getMeetingDate()));
			if (newSession != null) {
				map.put(new DataFieldName("sessionNo2"), ReportController.genChineseNum(newSession.getSessionNo()));
				map.put(new DataFieldName("date2"), genDateFormat1(newSession.getMeetingDate()));
				map.put(new DataFieldName("date2_2"), genDateFormat2(newSession.getMeetingDate()));
			}

			MailMerger.performMerge(template, map, true);
			File result = File.createTempFile("report6", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_光碟封面", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 光碟目次
	@RequestMapping(value = "/report/{sessionNo}/report7.htm", method = RequestMethod.GET)
	public void report7(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		Session session = sessionService.get(sessionNo);
		try {
			File tmp = new File(Global.APP.REPORT_PATH, "光碟目次.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);

			// 變數
			Map<DataFieldName, String> map = new HashMap<DataFieldName, String>();
			map.put(new DataFieldName("sessionNo"), sessionNo);
			map.put(new DataFieldName("date"), genDateFormat1(session.getMeetingDate()));

			Part part = template.getMainDocumentPart();
			List<Object> ps = getAllElementFromObject(part, P.class);

			Map<String, String> map2;
			String typeNo = "@";
			try {
				int count = 1;
				int result = 0;
				for (Motion motion : motions) {
					map2 = new HashMap<>();
					if (!StringUtils.equals(typeNo, motion.getTypeNo())) {
						if (StringUtils.equals(motion.getTypeNo(), "0")) {
							map2.put("案", "報告案");
							result = cloneP(template, ps, "案", map2, count);
							count = ((result == 0) ? 0 : count + result);
						} else if (StringUtils.equals(motion.getTypeNo(), "3")) {
							map2.put("案", "審議案");
							result = cloneP(template, ps, "案", map2, count);
							count = ((result == 0) ? 0 : count + result);
						} else {
							map2.put("案", "臨時動議");
							result = cloneP(template, ps, "案", map2, count);
							count = ((result == 0) ? 0 : count + result);
						}
						typeNo = motion.getTypeNo();
					}
					map2 = new HashMap<>();
					map2.put("案號", motion.getMotionNo());
					map2.put("案由", motion.getCause());
					map2.put("單位", codeUnitService.get(motion.getUnitCode()).getName());
					result = cloneP(template, ps, "案號", map2, count);
					count = ((result == 0) ? 0 : count + result);
				}
				String[] strs = { "案", "案號" };
				removeP(template, ps, strs);
			} catch (JAXBException e) {
				e.printStackTrace();
			}
			MailMerger.performMerge(template, map, true);
			File result = File.createTempFile("report4", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_光碟目次", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 確認單(單張)
	@RequestMapping(value = "/report/{motionNo}/report8single.htm", method = RequestMethod.GET)
	public void report8Single(@PathVariable String motionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		Motion motion = motionService.get(motionNo);
		Session nowSession = motion.getSession();
		try {
			// ============================格式模版============================
			File format = new File(Global.APP.REPORT_PATH, "格式三(確認單).docx");
			WordprocessingMLPackage formatTemp = WordprocessingMLPackage.load(format);
			Part formatPart = formatTemp.getMainDocumentPart();
			List<Object> formatPs = getAllElementFromObject(formatPart, P.class);
			P cause = getTemplateP(formatPs, DocCode.CAUSE.code);
			P d1 = getTemplateP(formatPs, DocCode.DECIDE1.code);
			P d2 = getTemplateP(formatPs, DocCode.DECIDE2.code);
			P d3 = getTemplateP(formatPs, DocCode.DECIDE3.code);
			P sub1 = getTemplateP(formatPs, DocCode.SUB1.code);
			P sub2 = getTemplateP(formatPs, DocCode.SUB2.code);
			P sub3 = getTemplateP(formatPs, DocCode.SUB3.code);
			// ============================格式模版============================
			File tmp = new File(Global.APP.REPORT_PATH, "確認單.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);

			Part tempPart = template.getMainDocumentPart();
			List<Object> tables = getAllElementFromObject(tempPart, Tbl.class);
			Tbl formatTable = getTemplateTable(tables, "案");
			// 變數
			Tbl tempTbl = XmlUtils.deepCopy(formatTable);
			Map<String, String> map = new HashMap<>();
			map.put("案", motion.getType() + genChineseNum(motion.getMotionOrder()));
			if (StringUtils.isNotBlank(motion.getExaminant())//
					&& StringUtils.split(motion.getExaminant(), ",").length != 0) {
				map.put("委員", committeeService.committeesName(motion.getExaminant()));
				replaceAndAddTable(tempTbl, Arrays.asList(map), 0);
				map = new HashMap<>();
				map.put("處", codeUnitService.get(motion.getUnitCode()).getName());
				replaceAndAddTable(tempTbl, Arrays.asList(map), 0);
			} else {
				map.put("審查委員：", "提案單位：");
				map.put("委員", codeUnitService.get(motion.getUnitCode()).getName());
				replaceAndAddTable(tempTbl, Arrays.asList(map), 0);
				map = new HashMap<>();
				map.put("提案單位：", "");
				map.put("處", "");
				replaceAndAddTable(tempTbl, Arrays.asList(map), 0);
			}
			template.getMainDocumentPart().getContent().set(0, tempTbl);
			// ============================案由============================
			Map<String, String> tempMap = new HashMap<>();
			tempMap.put(DocCode.CAUSE.code, String.format(DocCode.CAUSE.format, motion.getCause()));
			genDocP(cause, template, tempMap);
			// ============================案由============================
			// ============================決議============================
			tempMap = new HashMap<>();
			tempMap.put(DocCode.DECIDE1.code, //
					String.format(DocCode.DECIDE1.format,
							StringUtils.equals(motion.getTypeNo(), "0") ? "決定" : "決議",//
							StringUtils.startsWith(motion.getResolution(), "(一)") ? "" : StringUtils.isBlank(motion
									.getResolution()) ? "" : motion.getResolution()));
			genDocP(d1, template, tempMap);
			// ============================決議============================
			if (StringUtils.startsWith(motion.getResolution(), "(一)")) {
				String str = motion.getResolution();
				String[] strs = StringUtils.split(str, "\r\n");
				for (String s : strs) {
					tempMap = new HashMap<>();
					if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
						tempMap.put(DocCode.DECIDE2.code, String.format(DocCode.DECIDE2.format, s));
						genDocP(d2, template, tempMap);
					} else {
						tempMap.put(DocCode.DECIDE3.code, String.format(DocCode.DECIDE3.format, s));
						genDocP(d3, template, tempMap);
					}
				}
			}
			// ============================決議============================
			// ============================附議============================
			if (StringUtils.isNotBlank(motion.getSubResolution())) {
				tempMap = new HashMap<>();
				tempMap.put(DocCode.SUB1.code, //
						String.format(
								DocCode.SUB1.format, //
								StringUtils.startsWith(motion.getSubResolution(), "(一)") ? "" : StringUtils
										.isBlank(motion.getSubResolution()) ? "" : motion.getSubResolution()));
				genDocP(sub1, template, tempMap);
				if (StringUtils.startsWith(motion.getSubResolution(), "(一)")) {
					String str = motion.getSubResolution();
					String[] strs = StringUtils.split(str, "\r\n");
					for (String s : strs) {
						tempMap = new HashMap<>();
						if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
							tempMap.put(DocCode.SUB2.code, String.format(DocCode.SUB2.format, s));
							genDocP(sub2, template, tempMap);
						} else {
							tempMap.put(DocCode.SUB3.code, String.format(DocCode.SUB3.format, s));
							genDocP(sub3, template, tempMap);
						}
					}

				}
			}
			// ============================附議============================
			// ============================footer============================
			RelationshipsPart rp = template.getMainDocumentPart().getRelationshipsPart();
			Relationship rel = rp.getRelationshipByType(Namespaces.FOOTER);
			Part p = rp.getPart(rel);
			List<Object> tables1 = getAllElementFromObject(p, Tbl.class);
			Tbl tempTable;
			try {
				tempTable = getTemplateTable(tables1, "committee");
				List<Committee> committees = committeeService.findByCode(nowSession.getCommitteeTeam());
				if (CollectionUtils.isNotEmpty(committees)) {
					List<Map<String, String>> cList = new ArrayList<>();
					Map<String, String> cMap;
					for (Committee committee : committees) {
						cMap = new HashMap<>();
						cMap.put("committee", committee.getName());
						cList.add(cMap);
					}
					replaceAndAddTable(tempTable, cList, 0);// 表格row(0)為模組
				}
			} catch (JAXBException e) {
				e.printStackTrace();
			}
			// ============================footer============================
			Part mainPart = template.getMainDocumentPart();
			List<Object> allP = getAllElementFromObject(mainPart, P.class);
			List<Object> removeP = new ArrayList<>();
			for (Iterator<Object> iterator = allP.iterator(); iterator.hasNext();) {
				Object obj = iterator.next();
				List<?> textElements = getAllElementFromObject(obj, Text.class);
				for (Object text : textElements) {
					Text textElement = (Text) text;
					if (StringUtils.equals(textElement.getValue(), "r")) {
						removeP.add(obj);
					}
				}
			}
			for (Object obj : removeP) {
				template.getMainDocumentPart().getContent().remove(obj);
			}

			File result = File.createTempFile("report8Single", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(nowSession.getSessionNo() + "_確認單(" + motion.getMotionNo() + ")", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JAXBException e1) {
			e1.printStackTrace();
		}
	}

	// 確認單
	@RequestMapping(value = "/report/{sessionNo}/report8.htm", method = RequestMethod.GET)
	public void report8(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		Session nowSession = sessionService.get(sessionNo);
		try {
			// ============================格式模版============================
			File format = new File(Global.APP.REPORT_PATH, "格式三(確認單).docx");
			WordprocessingMLPackage formatTemp = WordprocessingMLPackage.load(format);
			Part formatPart = formatTemp.getMainDocumentPart();
			List<Object> formatPs = getAllElementFromObject(formatPart, P.class);
			P cause = getTemplateP(formatPs, DocCode.CAUSE.code);
			P d1 = getTemplateP(formatPs, DocCode.DECIDE1.code);
			P d2 = getTemplateP(formatPs, DocCode.DECIDE2.code);
			P d3 = getTemplateP(formatPs, DocCode.DECIDE3.code);
			P sub1 = getTemplateP(formatPs, DocCode.SUB1.code);
			P sub2 = getTemplateP(formatPs, DocCode.SUB2.code);
			P sub3 = getTemplateP(formatPs, DocCode.SUB3.code);
			// ============================格式模版============================
			File tmp = new File(Global.APP.REPORT_PATH, "確認單.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);

			Part tempPart = template.getMainDocumentPart();
			List<Object> tables = getAllElementFromObject(tempPart, Tbl.class);
			Tbl formatTable = getTemplateTable(tables, "案");
			// 變數
			Map<String, String> map = new HashMap<>();
			int count = 0;
			for (Motion motion : motions) {
				Tbl tempTbl = XmlUtils.deepCopy(formatTable);
				map.put("案", motion.getType() + genChineseNum(motion.getMotionOrder()));
				if (StringUtils.isNotBlank(motion.getExaminant())//
						&& StringUtils.split(motion.getExaminant(), ",").length != 0) {
					map.put("委員", committeeService.committeesName(motion.getExaminant()));
					replaceAndAddTable(tempTbl, Arrays.asList(map), 0);
					map = new HashMap<>();
					map.put("處", codeUnitService.get(motion.getUnitCode()).getName());
					replaceAndAddTable(tempTbl, Arrays.asList(map), 0);
				} else {
					map.put("審查委員：", "提案單位：");
					map.put("委員", codeUnitService.get(motion.getUnitCode()).getName());
					replaceAndAddTable(tempTbl, Arrays.asList(map), 0);
					map = new HashMap<>();
					map.put("提案單位：", "");
					map.put("處", "");
					replaceAndAddTable(tempTbl, Arrays.asList(map), 0);
				}
				if (count == 0) {
					template.getMainDocumentPart().getContent().set(0, tempTbl);
				} else {
					template.getMainDocumentPart().getContent().add(tempTbl);
				}
				// ============================案由============================
				Map<String, String> tempMap = new HashMap<>();
				tempMap.put(DocCode.CAUSE.code, String.format(DocCode.CAUSE.format, motion.getCause()));
				genDocP(cause, template, tempMap);
				// ============================案由============================
				// ============================決議============================
				tempMap = new HashMap<>();
				tempMap.put(DocCode.DECIDE1.code, //
						String.format(
								DocCode.DECIDE1.format,
								StringUtils.equals(motion.getTypeNo(), "0") ? "決定" : "決議",//
								StringUtils.startsWith(motion.getResolution(), "(一)") ? "" : StringUtils.isBlank(motion
										.getResolution()) ? "" : motion.getResolution()));
				genDocP(d1, template, tempMap);
				// ============================決議============================
				if (StringUtils.startsWith(motion.getResolution(), "(一)")) {
					String str = motion.getResolution();
					String[] strs = StringUtils.split(str, "\r\n");
					for (String s : strs) {
						tempMap = new HashMap<>();
						if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
							tempMap.put(DocCode.DECIDE2.code, String.format(DocCode.DECIDE2.format, s));
							genDocP(d2, template, tempMap);
						} else {
							tempMap.put(DocCode.DECIDE3.code, String.format(DocCode.DECIDE3.format, s));
							genDocP(d3, template, tempMap);
						}
					}
				}
				// ============================決議============================
				// ============================附議============================
				if (StringUtils.isNotBlank(motion.getSubResolution())) {
					tempMap = new HashMap<>();
					tempMap.put(DocCode.SUB1.code, //
							String.format(
									DocCode.SUB1.format, //
									StringUtils.startsWith(motion.getSubResolution(), "(一)") ? "" : StringUtils
											.isBlank(motion.getSubResolution()) ? "" : motion.getSubResolution()));
					genDocP(sub1, template, tempMap);
					if (StringUtils.startsWith(motion.getSubResolution(), "(一)")) {
						String str = motion.getSubResolution();
						String[] strs = StringUtils.split(str, "\r\n");
						for (String s : strs) {
							tempMap = new HashMap<>();
							if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
								tempMap.put(DocCode.SUB2.code, String.format(DocCode.SUB2.format, s));
								genDocP(sub2, template, tempMap);
							} else {
								tempMap.put(DocCode.SUB3.code, String.format(DocCode.SUB3.format, s));
								genDocP(sub3, template, tempMap);
							}
						}

					}
				}
				// ============================附議============================
				addPageBreak(template);
				count++;
			}
			// ============================footer============================
			RelationshipsPart rp = template.getMainDocumentPart().getRelationshipsPart();
			Relationship rel = rp.getRelationshipByType(Namespaces.FOOTER);
			Part p = rp.getPart(rel);
			List<Object> tables1 = getAllElementFromObject(p, Tbl.class);
			Tbl tempTable;
			try {
				tempTable = getTemplateTable(tables1, "committee");
				List<Committee> committees = committeeService.findByCode(nowSession.getCommitteeTeam());
				if (CollectionUtils.isNotEmpty(committees)) {
					List<Map<String, String>> cList = new ArrayList<>();
					Map<String, String> cMap;
					for (Committee committee : committees) {
						cMap = new HashMap<>();
						cMap.put("committee", committee.getName());
						cList.add(cMap);
					}
					replaceAndAddTable(tempTable, cList, 0);// 表格row(0)為模組
				}
			} catch (JAXBException e) {
				e.printStackTrace();
			}
			// ============================footer============================
			Part mainPart = template.getMainDocumentPart();
			List<Object> allP = getAllElementFromObject(mainPart, P.class);
			List<Object> removeP = new ArrayList<>();
			for (Iterator<Object> iterator = allP.iterator(); iterator.hasNext();) {
				Object obj = iterator.next();
				List<?> textElements = getAllElementFromObject(obj, Text.class);
				for (Object text : textElements) {
					Text textElement = (Text) text;
					if (StringUtils.equals(textElement.getValue(), "r")) {
						removeP.add(obj);
					}
				}
			}
			for (Object obj : removeP) {
				template.getMainDocumentPart().getContent().remove(obj);
			}
			File result = File.createTempFile("report8", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_確認單", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JAXBException e1) {
			e1.printStackTrace();
		}
	}

	// 紀錄
	@RequestMapping(value = "/report/{sessionNo}/report9.htm", method = RequestMethod.GET)
	public void report9(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		try {
			// ============================格式模版============================
			File format = new File(Global.APP.REPORT_PATH, "格式一(公報).docx");
			WordprocessingMLPackage formatTemp = WordprocessingMLPackage.load(format);
			Part formatPart = formatTemp.getMainDocumentPart();
			List<Object> formatPs = getAllElementFromObject(formatPart, P.class);
			P item1 = getTemplateP(formatPs, DocCode.ITEM1.code);
			P case1 = getTemplateP(formatPs, DocCode.CASE.code);
			P item2 = getTemplateP(formatPs, DocCode.ITEM2.code);
			P d1 = getTemplateP(formatPs, DocCode.DECIDE1.code);
			P d2 = getTemplateP(formatPs, DocCode.DECIDE2.code);
			P d3 = getTemplateP(formatPs, DocCode.DECIDE3.code);
			P sub1 = getTemplateP(formatPs, DocCode.SUB1.code);
			P sub2 = getTemplateP(formatPs, DocCode.SUB2.code);
			P sub3 = getTemplateP(formatPs, DocCode.SUB3.code);
			P tail3 = getTemplateP(formatPs, DocCode.TAIL3.code);
			P tail4 = getTemplateP(formatPs, DocCode.TAIL4.code);
			// ============================格式模版============================
			File tmp = new File(Global.APP.REPORT_PATH, "紀錄.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);

			// ============================分門別類============================
			Map<String, List<Motion>> temp = getNewMainMap();
			for (Motion motion : motions) {
				if (StringUtils.equals(motion.getTypeNo(), "0")) {
					List<Motion> list = temp.get("0");
					list.add(motion);
					temp.put("0", list);
				} else if (StringUtils.equals(motion.getTypeNo(), "3")) {
					List<Motion> list = temp.get("3");
					list.add(motion);
					temp.put("3", list);
				} else if (StringUtils.equals(motion.getTypeNo(), "9")) {
					List<Motion> list = temp.get("9");
					list.add(motion);
					temp.put("9", list);
				}
			}
			// ============================分門別類============================
			Set<Entry<String, List<Motion>>> entrySet = temp.entrySet();
			Map<String, String> tempMap = new HashMap<>();
			for (Entry<String, List<Motion>> entry : entrySet) {
				String key = entry.getKey();
				List<Motion> value = entry.getValue();
				// ============================事項標題============================
				if (CollectionUtils.isEmpty(value)) {
					if (StringUtils.equals(key, "0")) {// 報告案
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "柒", "報告事項", "無。"));
						genDocP(item1, template, tempMap);
					} else if (StringUtils.equals(key, "3")) {
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "捌", "討論事項", "無。"));
						genDocP(item1, template, tempMap);
					} else if (StringUtils.equals(key, "9")) {
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "玖", "臨時動議", "無。"));
						genDocP(item1, template, tempMap);
					}
				} else {
					if (StringUtils.equals(key, "0")) {// 報告案
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "柒", "報告事項", ""));
						genDocP(item1, template, tempMap);
						tempMap = new HashMap<>();
						tempMap.put(DocCode.CASE.code, String.format(DocCode.CASE.format, "報告案"));
						genDocP(case1, template, tempMap);
					} else if (StringUtils.equals(key, "3")) {// 審議案
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "捌", "討論事項", ""));
						genDocP(item1, template, tempMap);
						tempMap = new HashMap<>();
						tempMap.put(DocCode.CASE.code, String.format(DocCode.CASE.format, "審議案"));
						genDocP(case1, template, tempMap);
					} else if (StringUtils.equals(key, "9")) {// 臨時動議
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "玖", "臨時動議", ""));
						genDocP(item1, template, tempMap);
					}
				}
				for (Motion motion : value) {
					// ============================事項標題============================
					// ============================案由============================
					tempMap = new HashMap<>();
					tempMap.put(
							DocCode.ITEM2.code,
							String.format(DocCode.ITEM2.format, genChineseNum(motion.getMotionOrder()),
									motion.getCause()));
					genDocP(item2, template, tempMap);
					// ============================案由============================
					// ============================決議============================
					tempMap = new HashMap<>();
					tempMap.put(DocCode.DECIDE1.code, //
							String.format(DocCode.DECIDE1.format,
									StringUtils.equals(motion.getTypeNo(), "0") ? "決定" : "決議",//
									StringUtils.startsWith(motion.getResolution(), "(一)") ? "" : StringUtils
											.isBlank(motion.getResolution()) ? "" : motion.getResolution()));
					genDocP(d1, template, tempMap);
					// ============================決議============================
					if (StringUtils.startsWith(motion.getResolution(), "(一)")) {
						// if(motion.getResolution().matches("\\([一二三四五六七八九十]+\\)?.*")){
						String str = motion.getResolution();
						String[] strs = StringUtils.split(str, "\r\n");
						for (String s : strs) {
							tempMap = new HashMap<>();
							if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
								tempMap.put(DocCode.DECIDE2.code, String.format(DocCode.DECIDE2.format, s));
								genDocP(d2, template, tempMap);
							} else {
								tempMap.put(DocCode.DECIDE3.code, String.format(DocCode.DECIDE3.format, s));
								genDocP(d3, template, tempMap);
							}
						}
					}
					// ============================決議============================
					// ============================附議============================
					if (StringUtils.isNotBlank(motion.getSubResolution())) {
						tempMap = new HashMap<>();
						tempMap.put(DocCode.SUB1.code, //
								String.format(
										DocCode.SUB1.format, //
										StringUtils.startsWith(motion.getSubResolution(), "(一)") ? "" : StringUtils
												.isBlank(motion.getSubResolution()) ? "" : motion.getSubResolution()));
						genDocP(sub1, template, tempMap);
						if (StringUtils.startsWith(motion.getSubResolution(), "(一)")) {
							// if(motion.getResolution().matches("\\([一二三四五六七八九十]+\\)?.*")){
							String str = motion.getSubResolution();
							String[] strs = StringUtils.split(str, "\r\n");
							for (String s : strs) {
								tempMap = new HashMap<>();
								if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
									tempMap.put(DocCode.SUB2.code, String.format(DocCode.SUB2.format, s));
									genDocP(sub2, template, tempMap);
								} else {
									tempMap.put(DocCode.SUB3.code, String.format(DocCode.SUB3.format, s));
									genDocP(sub3, template, tempMap);

								}
							}

						}
					}
					// ============================附議============================
				}
			}
			// ============================tail============================
			P tempP = XmlUtils.deepCopy(tail3);
			template.getMainDocumentPart().getContent().add(tempP);
			tempP = XmlUtils.deepCopy(tail4);
			template.getMainDocumentPart().getContent().add(tempP);
			// ============================tail============================
			File result = File.createTempFile("report9", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_紀錄", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JAXBException e1) {
			e1.printStackTrace();
		}
	}

	// 錄案通知
	@RequestMapping(value = "/report/{sessionNo}/report10.htm", method = RequestMethod.GET)
	public void report10(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		Session nowSession = sessionService.get(sessionNo);
		try {
			// ============================格式模版============================
			File format = new File(Global.APP.REPORT_PATH, "格式四(錄案通知).docx");
			WordprocessingMLPackage formatTemp = WordprocessingMLPackage.load(format);
			Part formatPart = formatTemp.getMainDocumentPart();
			List<Object> formatPs = getAllElementFromObject(formatPart, P.class);
			// ============================格式模版============================

			File tmp = new File(Global.APP.REPORT_PATH, "錄案通知2.docx");
			WordprocessingMLPackage formatTemp2 = WordprocessingMLPackage.load(tmp);

			File tmp2 = new File(Global.APP.REPORT_PATH, "錄案通知.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp2);

			Part tempPart = formatTemp2.getMainDocumentPart();
			List<Object> tables = getAllElementFromObject(tempPart, Tbl.class);
			Tbl formatTable = getTemplateTable(tables, "公平交易委員會委員會議議決事項錄案通知書");
			// 變數
			Map<String, String> map = new HashMap<>();
			int count = 0;
			for (Motion motion : motions) {
				Tbl tempTbl = XmlUtils.deepCopy(formatTable);
				map.put("session", String.format("第%s次委員會議", nowSession.getSessionNo()));
				map.put("unita", codeUnitService.get(motion.getUnitCode()).getName());
				replaceAndAddTable(tempTbl, Arrays.asList(map), 1);
				map.put("date", genDateFormat1(nowSession.getMeetingDate()));
				replaceAndAddTable(tempTbl, Arrays.asList(map), 1);
				map.put("case", motion.getType());
				map.put("cause", motion.getCause());
				replaceAndAddTable(tempTbl, Arrays.asList(map), 1);
				map.put("r", "");
				genDecide(formatPs, motion, tempTbl, Arrays.asList(map), 1);
				replaceAndAddTable(tempTbl, Arrays.asList(map), 1);
				if (count == 0) {
					template.getMainDocumentPart().getContent().set(0, tempTbl);
				} else {
					template.getMainDocumentPart().getContent().add(tempTbl);
					
				}
				addPageBreak(template);
				count++;
			}
			File result = File.createTempFile("report10", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_錄案通知", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JAXBException e1) {
			e1.printStackTrace();
		}
	}

	// 紀錄報告
	@RequestMapping(value = "/report/{sessionNo}/report11.htm", method = RequestMethod.GET)
	public void report11(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		Session nowSession = sessionService.get(sessionNo);
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		try {
			// ============================格式模版============================
			File format = new File(Global.APP.REPORT_PATH, "格式二(紀錄報告).docx");
			WordprocessingMLPackage formatTemp = WordprocessingMLPackage.load(format);
			Part formatPart = formatTemp.getMainDocumentPart();
			List<Object> formatPs = getAllElementFromObject(formatPart, P.class);
			P item1 = getTemplateP(formatPs, DocCode.ITEM1.code);
			P case1 = getTemplateP(formatPs, DocCode.CASE.code);
			P item2 = getTemplateP(formatPs, DocCode.ITEM2.code);
			P d1 = getTemplateP(formatPs, DocCode.DECIDE1.code);
			P d2 = getTemplateP(formatPs, DocCode.DECIDE2.code);
			P d3 = getTemplateP(formatPs, DocCode.DECIDE3.code);
			P sub1 = getTemplateP(formatPs, DocCode.SUB1.code);
			P sub2 = getTemplateP(formatPs, DocCode.SUB2.code);
			P sub3 = getTemplateP(formatPs, DocCode.SUB3.code);
			P tail1 = getTemplateP(formatPs, DocCode.TAIL1.code);
			P tail2 = getTemplateP(formatPs, DocCode.TAIL2.code);
			// ============================格式模版============================
			File tmp = new File(Global.APP.REPORT_PATH, "紀錄報告.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);
			// 變數
			Map<DataFieldName, String> map = new HashMap<>();
			map.put(new DataFieldName("sessionNo"), nowSession.getSessionNo());
			map.put(new DataFieldName("date"), genDateFormat1(nowSession.getMeetingDate()));
			List<Committee> committees = committeeService.findByCode(nowSession.getCommitteeTeam());
			String committeeOne = CollectionUtils.isNotEmpty(committees) ? committees.get(0).getName() : "";
			map.put(new DataFieldName("committeeOne"), committeeOne);
			if (StringUtils.isNotBlank(nowSession.getRecorder())) {
				Users user = usersService.get(nowSession.getRecorder());
				map.put(new DataFieldName("recorder"), user == null ? "" : user.getName());
			}
			Part part = template.getMainDocumentPart();
			List<Object> ps = getAllElementFromObject(part, P.class);

			// ============================委員============================
			P tempP = getTemplateP(ps, "committees");
			int pos = template.getMainDocumentPart().getContent().indexOf(tempP);
			Map<String, String> tempMap = new HashMap<>();
			tempMap.put("committees", "");
			P newP = cloneP(tempP, tempMap);
			ObjectFactory objFactory = Context.getWmlObjectFactory();
			int count = 0;
			for (Committee committee : committees) {
				count++;
				R rspc = objFactory.createR();
				Text txt = objFactory.createText();
				txt.setValue(committee.getName());
				rspc.getContent().add(txt);
				if (count != committees.size()) {
					Br br = objFactory.createBr();
					rspc.getContent().add(br);
				}
				newP.getContent().add(rspc);
			}
			template.getMainDocumentPart().getContent().set(pos, newP);
			// ============================委員============================

			// ============================分門別類============================
			Map<String, List<Motion>> temp = getNewMainMap();
			for (Motion motion : motions) {
				if (StringUtils.equals(motion.getTypeNo(), "0")) {
					List<Motion> list = temp.get("0");
					list.add(motion);
					temp.put("0", list);
				} else if (StringUtils.equals(motion.getTypeNo(), "3")) {
					List<Motion> list = temp.get("3");
					list.add(motion);
					temp.put("3", list);
				} else if (StringUtils.equals(motion.getTypeNo(), "9")) {
					List<Motion> list = temp.get("9");
					list.add(motion);
					temp.put("9", list);
				}
			}
			// ============================分門別類============================

			Set<Entry<String, List<Motion>>> entrySet = temp.entrySet();
			for (Entry<String, List<Motion>> entry : entrySet) {
				String key = entry.getKey();
				List<Motion> value = entry.getValue();
				// ============================事項標題============================
				if (CollectionUtils.isEmpty(value)) {
					if (StringUtils.equals(key, "0")) {// 報告案
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "陸", "報告事項", "無。"));
						genDocP(item1, template, tempMap);
					} else if (StringUtils.equals(key, "3")) {
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "柒", "討論事項", "無。"));
						genDocP(item1, template, tempMap);
					} else if (StringUtils.equals(key, "9")) {
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "捌", "臨時動議", "無。"));
						genDocP(item1, template, tempMap);
					}
				} else {
					if (StringUtils.equals(key, "0")) {// 報告案
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "陸", "報告事項", ""));
						genDocP(item1, template, tempMap);
						tempMap = new HashMap<>();
						tempMap.put(DocCode.CASE.code, String.format(DocCode.CASE.format, "報告案"));
						genDocP(case1, template, tempMap);
					} else if (StringUtils.equals(key, "3")) {// 審議案
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "柒", "討論事項", ""));
						genDocP(item1, template, tempMap);
						tempMap = new HashMap<>();
						tempMap.put(DocCode.CASE.code, String.format(DocCode.CASE.format, "審議案"));
						genDocP(case1, template, tempMap);
					} else if (StringUtils.equals(key, "9")) {// 臨時動議
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "捌", "臨時動議", ""));
						genDocP(item1, template, tempMap);
					}
				}
				// ============================事項標題============================
				for (Motion motion : value) {
					// ============================案由============================
					tempMap = new HashMap<>();
					tempMap.put(
							DocCode.ITEM2.code,
							String.format(DocCode.ITEM2.format, genChineseNum(motion.getMotionOrder()),
									motion.getCause()));
					genDocP(item2, template, tempMap);
					// ============================案由============================
					// ============================決議============================
					tempMap = new HashMap<>();
					tempMap.put(DocCode.DECIDE1.code, //
							String.format(DocCode.DECIDE1.format,
									StringUtils.equals(motion.getTypeNo(), "0") ? "決定" : "決議",//
									StringUtils.startsWith(motion.getResolution(), "(一)") ? "" : StringUtils
											.isBlank(motion.getResolution()) ? "" : motion.getResolution()));
					genDocP(d1, template, tempMap);
					// ============================決議============================
					if (StringUtils.startsWith(motion.getResolution(), "(一)")) {
						// if(motion.getResolution().matches("\\([一二三四五六七八九十]+\\)?.*")){
						String str = motion.getResolution();
						String[] strs = StringUtils.split(str, "\r\n");
						for (String s : strs) {
							tempMap = new HashMap<>();
							if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
								tempMap.put(DocCode.DECIDE2.code, String.format(DocCode.DECIDE2.format, s));
								genDocP(d2, template, tempMap);
							} else {
								tempMap.put(DocCode.DECIDE3.code, String.format(DocCode.DECIDE3.format, s));
								genDocP(d3, template, tempMap);
							}

						}
					}
					// ============================決議============================
					// ============================附議============================
					if (StringUtils.isNotBlank(motion.getSubResolution())) {
						tempMap = new HashMap<>();
						tempMap.put(DocCode.SUB1.code, //
								String.format(
										DocCode.SUB1.format, //
										StringUtils.startsWith(motion.getSubResolution(), "(一)") ? "" : StringUtils
												.isBlank(motion.getSubResolution()) ? "" : motion.getSubResolution()));
						genDocP(sub1, template, tempMap);
						if (StringUtils.startsWith(motion.getSubResolution(), "(一)")) {
							// if(motion.getResolution().matches("\\([一二三四五六七八九十]+\\)?.*")){
							String str = motion.getSubResolution();
							String[] strs = StringUtils.split(str, "\r\n");
							for (String s : strs) {
								tempMap = new HashMap<>();
								if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
									tempMap.put(DocCode.SUB2.code, String.format(DocCode.SUB2.format, s));
									genDocP(sub2, template, tempMap);
								} else {
									tempMap.put(DocCode.SUB3.code, String.format(DocCode.SUB3.format, s));
									genDocP(sub3, template, tempMap);
								}

							}

						}
					}
					// ============================附議============================
				}

			}

			// ============================tail============================
			tempP = XmlUtils.deepCopy(tail1);
			template.getMainDocumentPart().getContent().add(tempP);
			tempP = XmlUtils.deepCopy(tail2);
			template.getMainDocumentPart().getContent().add(tempP);
			// ============================tail============================
			MailMerger.performMerge(template, map, true);
			File result = File.createTempFile("report11", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_紀錄報告", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JAXBException e1) {
			e1.printStackTrace();
		}
	}

	// 公報紀錄
	@RequestMapping(value = "/report/{sessionNo}/report12.htm", method = RequestMethod.GET)
	public void report12(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		Session nowSession = sessionService.get(sessionNo);
		List<Motion> motions = motionService.getBySessionNo(sessionNo);
		try {
			// ============================格式模版============================
			File format = new File(Global.APP.REPORT_PATH, "格式一(公報).docx");
			WordprocessingMLPackage formatTemp = WordprocessingMLPackage.load(format);
			Part formatPart = formatTemp.getMainDocumentPart();
			List<Object> formatPs = getAllElementFromObject(formatPart, P.class);
			P item1 = getTemplateP(formatPs, DocCode.ITEM1.code);
			P case1 = getTemplateP(formatPs, DocCode.CASE.code);
			P item2 = getTemplateP(formatPs, DocCode.ITEM2.code);
			P d1 = getTemplateP(formatPs, DocCode.DECIDE1.code);
			P d2 = getTemplateP(formatPs, DocCode.DECIDE2.code);
			P d3 = getTemplateP(formatPs, DocCode.DECIDE3.code);
			P sub1 = getTemplateP(formatPs, DocCode.SUB1.code);
			P sub2 = getTemplateP(formatPs, DocCode.SUB2.code);
			P sub3 = getTemplateP(formatPs, DocCode.SUB3.code);
			P tail1 = getTemplateP(formatPs, DocCode.TAIL1.code);
			P tail2 = getTemplateP(formatPs, DocCode.TAIL2.code);
			// ============================格式模版============================
			File tmp = new File(Global.APP.REPORT_PATH, "紀錄(公報).docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);
			// 變數
			Map<DataFieldName, String> map = new HashMap<>();
			map.put(new DataFieldName("sessionNo"), nowSession.getSessionNo());
			map.put(new DataFieldName("date"), genDateFormat1(nowSession.getMeetingDate()));
			List<Committee> committees = committeeService.findByCode(nowSession.getCommitteeTeam());
			String committeeOne = CollectionUtils.isNotEmpty(committees) ? committees.get(0).getName() : "";
			map.put(new DataFieldName("committeeOne"), committeeOne);
			if (StringUtils.isNotBlank(nowSession.getRecorder())) {
				Users user = usersService.get(nowSession.getRecorder());
				map.put(new DataFieldName("recorder"), user == null ? "" : user.getName());
			}
			Part part = template.getMainDocumentPart();
			List<Object> ps = getAllElementFromObject(part, P.class);

			// ============================委員============================
			P tempP = getTemplateP(ps, "committees");
			int pos = template.getMainDocumentPart().getContent().indexOf(tempP);
			Map<String, String> tempMap = new HashMap<>();
			tempMap.put("committees", "");
			P newP = cloneP(tempP, tempMap);
			ObjectFactory objFactory = Context.getWmlObjectFactory();
			int count = 0;
			for (Committee committee : committees) {
				count++;
				R rspc = objFactory.createR();
				Text txt = objFactory.createText();
				txt.setValue(committee.getName());
				rspc.getContent().add(txt);
				if (count != committees.size()) {
					Br br = objFactory.createBr();
					rspc.getContent().add(br);
				}
				newP.getContent().add(rspc);
			}
			template.getMainDocumentPart().getContent().set(pos, newP);
			// ============================委員============================

			// ============================分門別類============================
			Map<String, List<Motion>> temp = getNewMainMap();
			for (Motion motion : motions) {
				if (StringUtils.equals(motion.getTypeNo(), "0")) {
					List<Motion> list = temp.get("0");
					list.add(motion);
					temp.put("0", list);
				} else if (StringUtils.equals(motion.getTypeNo(), "3")) {
					List<Motion> list = temp.get("3");
					list.add(motion);
					temp.put("3", list);
				} else if (StringUtils.equals(motion.getTypeNo(), "9")) {
					List<Motion> list = temp.get("9");
					list.add(motion);
					temp.put("9", list);
				}
			}
			// ============================分門別類============================

			Set<Entry<String, List<Motion>>> entrySet = temp.entrySet();
			for (Entry<String, List<Motion>> entry : entrySet) {
				String key = entry.getKey();
				List<Motion> value = entry.getValue();
				// ============================事項標題============================
				if (CollectionUtils.isEmpty(value)) {
					if (StringUtils.equals(key, "0")) {// 報告案
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "陸", "報告事項", "無。"));
						genDocP(item1, template, tempMap);
					} else if (StringUtils.equals(key, "3")) {
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "柒", "討論事項", "無。"));
						genDocP(item1, template, tempMap);
					} else if (StringUtils.equals(key, "9")) {
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "捌", "臨時動議", "無。"));
						genDocP(item1, template, tempMap);
					}
				} else {
					if (StringUtils.equals(key, "0")) {// 報告案
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "陸", "報告事項", ""));
						genDocP(item1, template, tempMap);
						tempMap = new HashMap<>();
						tempMap.put(DocCode.CASE.code, String.format(DocCode.CASE.format, "報告案"));
						genDocP(case1, template, tempMap);
					} else if (StringUtils.equals(key, "3")) {// 審議案
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "柒", "討論事項", ""));
						genDocP(item1, template, tempMap);
						tempMap = new HashMap<>();
						tempMap.put(DocCode.CASE.code, String.format(DocCode.CASE.format, "審議案"));
						genDocP(case1, template, tempMap);
					} else if (StringUtils.equals(key, "9")) {// 臨時動議
						tempMap.put(DocCode.ITEM1.code, String.format(DocCode.ITEM1.format, "捌", "臨時動議", ""));
						genDocP(item1, template, tempMap);
					}
				}
				// ============================事項標題============================
				for (Motion motion : value) {
					// ============================案由============================
					tempMap = new HashMap<>();
					tempMap.put(
							DocCode.ITEM2.code,
							String.format(DocCode.ITEM2.format, genChineseNum(motion.getMotionOrder()),
									motion.getCause()));
					genDocP(item2, template, tempMap);
					// ============================案由============================
					// ============================決議============================
					tempMap = new HashMap<>();
					tempMap.put(DocCode.DECIDE1.code, //
							String.format(DocCode.DECIDE1.format,
									StringUtils.equals(motion.getTypeNo(), "0") ? "決定" : "決議",//
									StringUtils.startsWith(motion.getResolution(), "(一)") ? "" : StringUtils
											.isBlank(motion.getResolution()) ? "" : motion.getResolution()));
					genDocP(d1, template, tempMap);
					// ============================決議============================
					if (StringUtils.startsWith(motion.getResolution(), "(一)")) {
						String str = motion.getResolution();
						String[] strs = StringUtils.split(str, "\r\n");
						for (String s : strs) {
							tempMap = new HashMap<>();
							if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
								tempMap.put(DocCode.DECIDE2.code, String.format(DocCode.DECIDE2.format, s));
								genDocP(d2, template, tempMap);
							} else {
								tempMap.put(DocCode.DECIDE3.code, String.format(DocCode.DECIDE3.format, s));
								genDocP(d3, template, tempMap);
							}
						}
					}
					// ============================決議============================
					// ============================附議============================
					if (StringUtils.isNotBlank(motion.getSubResolution())) {
						tempMap = new HashMap<>();
						tempMap.put(DocCode.SUB1.code, //
								String.format(
										DocCode.SUB1.format, //
										StringUtils.startsWith(motion.getSubResolution(), "(一)") ? "" : StringUtils
												.isBlank(motion.getSubResolution()) ? "" : motion.getSubResolution()));
						genDocP(sub1, template, tempMap);
						if (StringUtils.startsWith(motion.getSubResolution(), "(一)")) {
							String str = motion.getSubResolution();
							String[] strs = StringUtils.split(str, "\r\n");
							for (String s : strs) {
								tempMap = new HashMap<>();
								if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
									tempMap.put(DocCode.SUB2.code, String.format(DocCode.SUB2.format, s));
									genDocP(sub2, template, tempMap);
								} else {
									tempMap.put(DocCode.SUB3.code, String.format(DocCode.SUB3.format, s));
									genDocP(sub3, template, tempMap);
								}
							}

						}
					}
					// ============================附議============================
				}

			}
			// ============================tail============================
			tempP = XmlUtils.deepCopy(tail1);
			template.getMainDocumentPart().getContent().add(tempP);
			tempP = XmlUtils.deepCopy(tail2);
			template.getMainDocumentPart().getContent().add(tempP);
			// ============================tail============================
			MailMerger.performMerge(template, map, true);
			File result = File.createTempFile("report12", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_紀錄(公報)", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JAXBException e1) {
			e1.printStackTrace();
		}
	}

	// 錄音清單
	@RequestMapping(value = "/report/{sessionNo}/report13.htm", method = RequestMethod.GET)
	public void report13(@PathVariable String sessionNo, ModelMap model, HttpServletRequest request,
			HttpServletResponse response) {
		logService.before();
		Session nowSession = sessionService.get(sessionNo);
		try {
			File tmp = new File(Global.APP.REPORT_PATH, "錄音清單.docx");
			WordprocessingMLPackage template = WordprocessingMLPackage.load(tmp);
			// 變數
			Map<DataFieldName, String> map = new HashMap<>();
			map.put(new DataFieldName("sessionNo"), genChineseNum(nowSession.getSessionNo()));
			map.put(new DataFieldName("date"), genDateFormat3(nowSession.getMeetingDate(), "/"));

			Part p = template.getMainDocumentPart();
			List<Object> tables = getAllElementFromObject(p, Tbl.class);
			Tbl tempTable;
			try {
				tempTable = getTemplateTable(tables, "提案單位");
				List<Motion> motions = motionService.getBySessionNo(sessionNo);
				if (CollectionUtils.isNotEmpty(motions)) {
					List<Map<String, String>> cList = new ArrayList<>();
					Map<String, String> cMap;
					for (Motion motion : motions) {
						cMap = new HashMap<>();
						cMap.put("motionNo", motion.getMotionNo());
						cMap.put("unitName", codeUnitService.get(motion.getUnitCode()).getName());
						cMap.put("cause", motion.getCause());
						cList.add(cMap);
					}
					replaceAndAddTable(tempTable, cList, 2);// 表格row(2)為模組
				}
			} catch (JAXBException e) {
				e.printStackTrace();
			}
			MailMerger.performMerge(template, map, true);
			File result = File.createTempFile("report13", UUID.randomUUID() + ".docx");
			template.save(result);
			String name = URLEncoder.encode(sessionNo + "_錄音清單", "UTF-8");
			response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
			response.setHeader("Expires:", "0"); // eliminates browser caching
			response.setHeader("content-disposition", "attachment; filename=" + name + ".docx");
			FileCopyUtils.copy(new FileInputStream(result), response.getOutputStream());
			logService.save(Log.EXP, this.getClass().toString());
			response.flushBuffer();
		} catch (Docx4JException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// ======================================================================================//
	// tools //
	// ======================================================================================//

	/**
	 * 阿拉伯數字轉中文數字
	 * 
	 * @param original
	 * @return
	 */
	private static String genChineseNum(String original) {
		return StringUtils.replaceEach(original, NUM, CHINESE_NUM);
	}

	/**
	 * 103年1月1日
	 * 
	 * @param session
	 * @return
	 */
	private static String genDateFormat1(Date date) {
		if (date == null) {
			return "";
		}
		DateTime dateTime = new DateTime(date);
		return String.format("%s年%s月%s日", (dateTime.getYear() - 1911) + "", dateTime.getMonthOfYear() + "",
				dateTime.getDayOfMonth() + "");
	}

	/**
	 * 一０三.一.一
	 * 
	 * @param session
	 * @return
	 */
	private static String genDateFormat2(Date date) {
		if (date == null) {
			return "";
		}
		String result = genDateFormat3(date, ".");
		return genChineseNum(result);
	}

	/**
	 * 103.1.1
	 * 
	 * @param session
	 * @return
	 */
	private static String genDateFormat3(Date date, String split) {
		if (date == null) {
			return "";
		}
		DateTime dateTime = new DateTime(date);
		return StringUtils.join(new String[] { (dateTime.getYear() - 1911) + "", dateTime.getMonthOfYear() + "",
				dateTime.getDayOfMonth() + "" }, split);
	}

	/**
	 * 103年3月5日（星期三）
	 * 
	 * @param session
	 * @return
	 */
	private static String genDateFormat4(Date date) {
		DateTime dateTime = new DateTime(date);
		return String.format("%s年%s月%s日（%s）", (dateTime.getYear() - 1911) + "", dateTime.getMonthOfYear() + "",
				dateTime.getDayOfMonth() + "", genChineseDayOfWeek(dateTime));

	}

	private static String genChineseDayOfWeek(DateTime dt) {
		switch (dt.getDayOfWeek()) {
		case DateTimeConstants.SUNDAY:
			return "星期日";
		case DateTimeConstants.MONDAY:
			return "星期一";
		case DateTimeConstants.TUESDAY:
			return "星期二";
		case DateTimeConstants.WEDNESDAY:
			return "星期三";
		case DateTimeConstants.THURSDAY:
			return "星期四";
		case DateTimeConstants.FRIDAY:
			return "星期五";
		case DateTimeConstants.SATURDAY:
			return "星期六";
		default:
			return "";

		}
	}

	/**
	 * 
	 * @param template
	 * @param ps
	 * @param key
	 * @param map
	 * @throws Docx4JException
	 * @throws JAXBException
	 */
	private static void cloneP(WordprocessingMLPackage template, List<Object> ps, String key, Map<String, String> map)
			throws Docx4JException, JAXBException {
		P tempP = getTemplateP(ps, key);
		List<Object> content = template.getMainDocumentPart().getContent();
		P newP = (P) XmlUtils.deepCopy(tempP);
		List<?> textElements = getAllElementFromObject(newP, Text.class);
		for (Object object : textElements) {
			Text text = (Text) object;
			String replacementValue = (String) map.get(text.getValue());
			if (replacementValue != null) {
				text.setValue(replacementValue);
			}
		}
		content.add(newP);
	}

	/**
	 * 光碟目次(only)
	 * 
	 * @param template
	 * @param ps
	 * @param key
	 * @param map
	 * @param count
	 * @return
	 * @throws Docx4JException
	 * @throws JAXBException
	 */
	private static int cloneP(WordprocessingMLPackage template, List<Object> ps, String key, Map<String, String> map,
			int count) throws Docx4JException, JAXBException {
		P tempP = getTemplateP(ps, key);
		List<Object> content = template.getMainDocumentPart().getContent();
		P newP = (P) XmlUtils.deepCopy(tempP);
		List<?> textElements = getAllElementFromObject(newP, Text.class);
		int length = 0;
		for (Object object : textElements) {
			Text text = (Text) object;
			String replacementValue = (String) map.get(text.getValue());
			if (replacementValue != null) {
				length += replacementValue.length();
				text.setValue(replacementValue);
			}
		}
		boolean result = true;
		int line = ((length - 7) / 37) + 1;
		int num = (count + line) - 27;
		if (num > 0) {
			result = false;
			for (int j = 0; j < (43 + num); j++) {
				newP(template, ps, "案號");
			}
		}
		content.add(newP);
		return result ? (count == 0 ? 0 : line) : 0;
	}

	private static void newP(WordprocessingMLPackage template, List<Object> ps, String key) throws Docx4JException,
			JAXBException {
		P tempP = getTemplateP(ps, key);
		List<Object> content = template.getMainDocumentPart().getContent();
		P newP = (P) XmlUtils.deepCopy(tempP);
		List<?> textElements = getAllElementFromObject(newP, Text.class);
		for (Object object : textElements) {
			Text text = (Text) object;
			text.setValue("");
		}
		content.add(newP);
	}

	/**
	 * 
	 * @param template
	 * @param ps
	 * @param strs
	 * @throws Docx4JException
	 * @throws JAXBException
	 */
	private static void removeP(WordprocessingMLPackage template, List<Object> ps, String[] strs)
			throws Docx4JException, JAXBException {
		for (String key : strs) {
			P tempP = getTemplateP(ps, key);
			template.getMainDocumentPart().getContent().remove(tempP);
		}
	}


	/**
	 * 
	 * @param ps
	 * @param templateKey
	 * @return
	 * @throws Docx4JException
	 * @throws JAXBException
	 */
	private static P getTemplateP(List<Object> ps, String templateKey) throws Docx4JException, JAXBException {
		for (Iterator<Object> iterator = ps.iterator(); iterator.hasNext();) {
			Object p = iterator.next();
			List<?> textElements = getAllElementFromObject(p, Text.class);
			for (Object text : textElements) {
				Text textElement = (Text) text;
				if (StringUtils.equals(textElement.getValue(), templateKey))
					return (P) p;
			}
		}
		return null;
	}

	private static R getTemplateR(List<Object> ps, String templateKey) throws Docx4JException, JAXBException {
		for (Iterator<Object> iterator = ps.iterator(); iterator.hasNext();) {
			Object p = iterator.next();
			List<?> textElements = getAllElementFromObject(p, Text.class);
			for (Object text : textElements) {
				Text textElement = (Text) text;
				if (StringUtils.equals(textElement.getValue(), templateKey))
					return (R) p;
			}
		}
		return null;
	}

	/**
	 * 目次表四only
	 * 
	 * @param tempTable
	 * @param textToAdd
	 * @param template
	 * @throws Docx4JException
	 * @throws JAXBException
	 */
	private static void replaceAndCloneTr(Tbl tempTable, List<Map<String, String>> textToAdd,
			WordprocessingMLPackage template) throws Docx4JException, JAXBException {
		// 1. find the table

		List<Object> rows = getAllElementFromObject(tempTable, Tr.class);
		Tr templateRow = (Tr) rows.get(1);
		for (Map<String, String> replacements : textToAdd) {
			Tr workingrow = (Tr) XmlUtils.deepCopy(templateRow);
			List<?> textElements = getAllElementFromObject(workingrow, Text.class);
			for (Object object : textElements) {
				Text text = (Text) object;
				String replacementValue = replacements.get(text.getValue());
				if (replacementValue != null) {
					if (StringUtils.equals(text.getValue(), "committees")) {
						genCommittee(replacements, workingrow);
					} else {
						text.setValue(replacementValue);
					}
				} else {
					removeContent(text.getValue(), workingrow);
				}

			}
			tempTable.getContent().add(workingrow);
		}
		tempTable.getContent().remove(templateRow);
	}

	/**
	 * 目次表四(only)：產生表格中委員
	 * 
	 * @param replacements
	 * @param workingrow
	 * @throws Docx4JException
	 * @throws JAXBException
	 */
	private static void genCommittee(Map<String, String> replacements, Tr workingrow) throws Docx4JException,
			JAXBException {
		// --------------------------------
		List<Object> rs = getAllElementFromObject(workingrow, R.class);
		List<Object> ps = getAllElementFromObject(workingrow, P.class);
		ObjectFactory objFactory = Context.getWmlObjectFactory();
		P tempP = getTemplateP(ps, "committees");
		R tempR = getTemplateR(rs, "committees");
		int pos = tempP.getContent().indexOf(tempR);
		Map<String, String> map = new HashMap<>();
		map.put("committees", "");
		R newR = cloneR(tempR, map);
		String committees = replacements.get("committees");
		String[] strs = StringUtils.split(committees, "、");
		int count = 0;
		for (String str : strs) {
			count++;
			Text txt = objFactory.createText();
			txt.setValue(str);
			newR.getContent().add(txt);
			if (count < strs.length) {
				Br br = objFactory.createBr();
				newR.getContent().add(br);
			}
		}
		tempP.getContent().set(pos, newR);
		// --------------------------------
	}

	private static void removeContent(String remove, Tr workingrow) throws Docx4JException, JAXBException {
		// --------------------------------
		List<Object> ps = getAllElementFromObject(workingrow, P.class);
		P tempP = getTemplateP(ps, remove);
		Tc tc = (Tc) tempP.getParent();
		tc.getContent().remove(tempP);
		// --------------------------------
	}

	/**
	 * 
	 * @param tempTable
	 * @param textToAdd
	 * @param template
	 * @throws Docx4JException
	 * @throws JAXBException
	 */
	private static void replaceAndAddTable(Tbl tempTable, List<Map<String, String>> textToAdd, int modelRow)
			throws Docx4JException, JAXBException {
		// 1. find the table
		List<Object> rows = getAllElementFromObject(tempTable, Tr.class);
		Tr templateRow = (Tr) rows.get(modelRow);
		for (Map<String, String> replacements : textToAdd) {
			// 2 and 3 are done in this method
			addRowToTable(tempTable, templateRow, replacements);
		}
		// 4. remove the template row
		tempTable.getContent().remove(templateRow);
	}

	/**
	 * 
	 * @param obj
	 * @param toSearch
	 * @return
	 */
	private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
		List<Object> result = new ArrayList<Object>();
		if (obj instanceof JAXBElement)
			obj = ((JAXBElement<?>) obj).getValue();

		if (obj.getClass().equals(toSearch))
			result.add(obj);
		else if (obj instanceof ContentAccessor) {
			List<?> children = ((ContentAccessor) obj).getContent();
			for (Object child : children) {
				result.addAll(getAllElementFromObject(child, toSearch));
			}

		}
		return result;
	}

	/**
	 * 
	 * @param tables
	 * @param templateKey
	 * @return
	 * @throws Docx4JException
	 * @throws JAXBException
	 */
	private static Tbl getTemplateTable(List<Object> tables, String templateKey) throws Docx4JException, JAXBException {
		for (Iterator<Object> iterator = tables.iterator(); iterator.hasNext();) {
			Object tbl = iterator.next();
			List<?> textElements = getAllElementFromObject(tbl, Text.class);
			for (Object text : textElements) {
				Text textElement = (Text) text;
				if (StringUtils.equals(textElement.getValue(), templateKey))
					return (Tbl) tbl;
			}
		}
		return null;
	}

	/**
	 * 
	 * @param reviewtable
	 * @param templateRow
	 * @param replacements
	 */
	private static void addRowToTable(Tbl reviewtable, Tr templateRow, Map<String, String> replacements) {
		Tr workingRow = (Tr) XmlUtils.deepCopy(templateRow);
		List<?> textElements = getAllElementFromObject(workingRow, Text.class);
		for (Object object : textElements) {
			Text text = (Text) object;
			String replacementValue = (String) replacements.get(text.getValue());
			if (replacementValue != null) {
				text.setValue(replacementValue);
			}
		}
		reviewtable.getContent().add(workingRow);
	}

	/**
	 * 複製並置換內容
	 * 
	 * @param p
	 * @param replacements
	 * @return
	 */
	private static P cloneP(P p, Map<String, String> replacements) {
		P tempP = (P) XmlUtils.deepCopy(p);
		List<?> textElements = getAllElementFromObject(tempP, Text.class);
		for (Object object : textElements) {
			Text text = (Text) object;
			String replacementValue = (String) replacements.get(text.getValue());
			if (replacementValue != null) {
				text.setValue(replacementValue);
			}
		}
		return tempP;
	}

	private static R cloneR(R r, Map<String, String> replacements) {
		R tempR = (R) XmlUtils.deepCopy(r);
		List<?> textElements = getAllElementFromObject(tempR, Text.class);
		for (Object object : textElements) {
			Text text = (Text) object;
			String replacementValue = replacements.get(text.getValue());
			if (replacementValue != null) {
				text.setValue(replacementValue);
			}
		}
		return tempR;
	}

	private void genDocP(P item1, WordprocessingMLPackage template, Map<String, String> tempMap) {
		P tempP = XmlUtils.deepCopy(item1);
		P newP = cloneP(tempP, tempMap);
		template.getMainDocumentPart().getContent().add(newP);
	}

	private static P getCloneP(P item1, Map<String, String> tempMap) {
		P tempP = XmlUtils.deepCopy(item1);
		P newP = cloneP(tempP, tempMap);
		return newP;
	}

	/**
	 * 換頁
	 * 
	 * @param template
	 */
	private static void addPageBreak(WordprocessingMLPackage template) {
		org.docx4j.wml.P p = new org.docx4j.wml.P();
		org.docx4j.wml.R r = new org.docx4j.wml.R();
		org.docx4j.wml.Br br = new org.docx4j.wml.Br();
		br.setType(STBrType.PAGE);
		r.getContent().add(br);
		p.getContent().add(r);
		template.getMainDocumentPart().addObject(p);
	}

	/**
	 * 錄案通知(only)
	 * 
	 * @param tempTable
	 * @param textToAdd
	 * @param index
	 * @throws Docx4JException
	 * @throws JAXBException
	 */
	private static void genDecide(List<Object> formatPs, Motion motion, Tbl tempTable,
			List<Map<String, String>> textToAdd, int index) throws Docx4JException, JAXBException {
		List<Object> rows = getAllElementFromObject(tempTable, Tr.class);
		Tr templateRow = (Tr) rows.get(index);
		for (Map<String, String> replacements : textToAdd) {
			addRowToTable2(formatPs, motion, tempTable, templateRow, replacements);
		}
		tempTable.getContent().remove(templateRow);
	}

	private static void addRowToTable2(List<Object> formatPs, Motion motion, Tbl reviewtable, Tr templateRow,
			Map<String, String> replacements) throws Docx4JException, JAXBException {
		// ============================格式模版============================

		P d1 = getTemplateP(formatPs, DocCode.DECIDE1.code);
		P d2 = getTemplateP(formatPs, DocCode.DECIDE2.code);
		P d3 = getTemplateP(formatPs, DocCode.DECIDE3.code);
		P sub1 = getTemplateP(formatPs, DocCode.SUB1.code);
		P sub2 = getTemplateP(formatPs, DocCode.SUB2.code);
		P sub3 = getTemplateP(formatPs, DocCode.SUB3.code);
		// ============================格式模版============================
		Tr workingRow = (Tr) XmlUtils.deepCopy(templateRow);
		List<?> ps = getAllElementFromObject(workingRow, P.class);
		for (Object obj : ps) {
			P tempP = (P) obj;
			Tc tc = (Tc) tempP.getParent();
			List<?> textElements = getAllElementFromObject(tempP, Text.class);
			for (Object object : textElements) {
				Text text = (Text) object;
				String replacementValue = (String) replacements.get(text.getValue());
				if (replacementValue != null) {
//					text.setValue(replacementValue);
					tc.getContent().remove(0);
					// ============================決議============================
					Map<String, String> tempMap = new HashMap<>();
					tempMap.put(DocCode.DECIDE1.code, //
							String.format(DocCode.DECIDE1.format,
									StringUtils.equals(motion.getTypeNo(), "0") ? "決定" : "決議",//
									StringUtils.startsWith(motion.getResolution(), "(一)") ? "" : StringUtils
											.isBlank(motion.getResolution()) ? "" : motion.getResolution()));
					tc.getContent().add(getCloneP(d1, tempMap));
					// ============================決議============================
					if (StringUtils.startsWith(motion.getResolution(), "(一)")) {
						String str = motion.getResolution();
						String[] strs = StringUtils.split(str, "\r\n");
						for (String s : strs) {
							tempMap = new HashMap<>();
							if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
								tempMap.put(DocCode.DECIDE2.code, String.format(DocCode.DECIDE2.format, s));
								tc.getContent().add(getCloneP(d2, tempMap));
							} else {
								tempMap.put(DocCode.DECIDE3.code, String.format(DocCode.DECIDE3.format, s));
								tc.getContent().add(getCloneP(d3, tempMap));
							}
						}
					}
					// ============================決議============================
					// ============================附議============================
					if (StringUtils.isNotBlank(motion.getSubResolution())) {
						tempMap = new HashMap<>();
						tempMap.put(DocCode.SUB1.code, //
								String.format(
										DocCode.SUB1.format, //
										StringUtils.startsWith(motion.getSubResolution(), "(一)") ? "" : StringUtils
												.isBlank(motion.getSubResolution()) ? "" : motion.getSubResolution()));
						tc.getContent().add(getCloneP(sub1, tempMap));
						if (StringUtils.startsWith(motion.getSubResolution(), "(一)")) {
							String str = motion.getSubResolution();
							String[] strs = StringUtils.split(str, "\r\n");
							for (String s : strs) {
								tempMap = new HashMap<>();
								if (s.matches("\\([一二三四五六七八九十]+\\)?.*")) {
									tempMap.put(DocCode.SUB2.code, String.format(DocCode.SUB2.format, s));
									tc.getContent().add(getCloneP(sub2, tempMap));
								} else {
									tempMap.put(DocCode.SUB3.code, String.format(DocCode.SUB3.format, s));
									tc.getContent().add(getCloneP(sub3, tempMap));
								}
							}

						}
					}
					// ============================附議============================

				}
			}

		}
		reviewtable.getContent().add(workingRow);
	}

}
